/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.project.engineering.services;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import com.google.gson.Gson;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.apache.tapestry5.ioc.annotations.Inject;
import pl.project.engineering.data.SensorsDto;

/**
 *
 * @author ksmolnicki
 */
@Slf4j
public class Repository {

  private int limit;
  @Inject
  private AppConfig appConfig;
  private LinkedList data;
  private final Object lock = new Object();

  public Repository() {
    this.limit = 43200;
    data = Lists.newLinkedList();
  }

  @Synchronized("lock")
  public void addMeasurment(SensorsDto dto) {
    if (data.size() > limit) {
      data.remove();
    }
    data.add(dto);
    logData(dto);
  }

  private void logData(SensorsDto dto) {
    Double warnTemp = appConfig.getMailServerConfig().getWarningTemp();
    Double warnHumd = appConfig.getMailServerConfig().getWarningHumd();

    String temperature = Joiner.on(';').skipNulls().join(dto.getTemp());

    String message = String.format("Sensor -> temperature: %s; humidity: %s;  dew_point: %s;", temperature, dto.getHumd(), dto.getDewPoint());

    StringBuilder warnMessage = new StringBuilder();

    for (int i = 0; i < dto.getTemp().length; i++) {
      warnMessage.append(checkParameter("Temperature" + i, dto.getTemp()[i], warnTemp));
    }
    warnMessage.append(checkParameter("Humidity", dto.getHumd(), warnHumd));

    if (warnMessage.length() == 0) {
      log.info(message);
    } else {
      log.error(message + "\n" + warnMessage);
    }
  }

  private String checkParameter(String property, Double actualValue, Double warningValue) {
    if (actualValue >= warningValue) {
      return String.format("%s: %.2f is to high;", property, actualValue);
    } else {
      return "";
    }
  }

  @Synchronized("lock")
  public SensorsDto getLasMeasurment() {
    if (data.isEmpty()) {
      return new SensorsDto(0, new Double[]{0.0}, 0, 0);
    } else {
      return (SensorsDto) data.getLast();
    }
  }

  @Synchronized("lock")
  public String getJSONData() {
    List<Map<?, ?>> chartObjects = Lists.newArrayList();
    Map<String, Object> mapDataHumi = Maps.newHashMap();
    mapDataHumi.put("values", getHumdData());
    mapDataHumi.put("type", "line");
    mapDataHumi.put("yAxis", 2);
    mapDataHumi.put("key", "Humidity");
    mapDataHumi.put("color", "#EE0000");
    
    Map<String, Object> mapDataDewPoint = Maps.newHashMap();
    mapDataDewPoint.put("values", getDewPointData());
    mapDataDewPoint.put("type", "line");
    mapDataDewPoint.put("yAxis", 1);
    mapDataDewPoint.put("key", "Dew point");
    mapDataDewPoint.put("color", "#000000");

    String[] colors = new String[]{"#008000", "#3A5FCD", "#CDCD00", "#ff7f0e"};
    for (int i = 0; i < ((SensorsDto) data.getFirst()).getTemp().length; i++) {
      Map<String, Object> mapDataTemp = Maps.newHashMap();
      Object[] tempData = getTempData(i);
      if (tempData.length != 0) {
        mapDataTemp.put("values", tempData);
        mapDataTemp.put("type", "line");
        mapDataTemp.put("yAxis", 1);
        mapDataTemp.put("key", "Temperature" + i);
        mapDataTemp.put("color", colors[i % 5]);
        chartObjects.add(mapDataTemp);
      }
    }
    chartObjects.add(mapDataDewPoint);
    chartObjects.add(mapDataHumi);
    return new Gson().toJson(chartObjects).toString();
  }

  private Object[] getHumdData() {
    return Collections2.filter(Lists.transform(data, new Function<SensorsDto, ChartData>() {
      @Override
      public ChartData apply(SensorsDto input) {
        return new ChartData(input.getTime(), input.getHumd() / 100);
      }
    }), Predicates.notNull()).toArray();
  }
  
  private Object[] getDewPointData() {
    return Collections2.filter(Lists.transform(data, new Function<SensorsDto, ChartData>() {
      @Override
      public ChartData apply(SensorsDto input) {
        return new ChartData(input.getTime(), input.getDewPoint());
      }
    }), Predicates.notNull()).toArray();
  }

  private Object[] getTempData(final int idx) {
    return Collections2.filter(Lists.transform(data, new Function<SensorsDto, ChartData>() {
      @Override
      public ChartData apply(SensorsDto input) {
        return input.getTemp()[idx] == -200 ? null : new ChartData(input.getTime(), input.getTemp()[idx]);
      }
    }), Predicates.notNull()).toArray();
  }

  @RequiredArgsConstructor
  private class ChartData {

    private final long x;
    private final double y;
  }
}
